package ormpool

import (
	"errors"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"sync"
	"sync/atomic"
)

var num int64

type DB = *gorm.DB

type OrmKey int64

type OrmFac interface {
	Init(schema string, option ...Option) (Orm, error)
	Release(key OrmKey) error
	GetOrm(key OrmKey) (Orm, bool)
}
type ormFac struct {
	// 统计数据

	rwm     sync.RWMutex // 下面的加锁操作字段
	ormPool map[OrmKey]Orm
}

func newOrmFac() OrmFac {
	return &ormFac{
		ormPool: make(map[OrmKey]Orm),
	}
}
func (o *ormFac) GetOrm(key OrmKey) (orm Orm, exist bool) {
	o.rwm.RLock()
	orm, exist = o.ormPool[key]
	o.rwm.RUnlock()
	return
}

func (o *ormFac) Init(schema string, option ...Option) (Orm, error) {
	o.rwm.Lock()
	defer o.rwm.Unlock()
	om, err := newOrm(schema, option...)
	if err != nil {
		return nil, err
	}
	o.ormPool[om.GetKey()] = om
	return om, nil
}

// 等待gc删除
func (o *ormFac) Release(key OrmKey) error {
	o.rwm.Lock()
	defer o.rwm.Unlock()
	om, ok := o.ormPool[key]
	if !ok {
		return nil
	}
	delete(o.ormPool, key)
	return om.release()
}

type Orm interface {
	GetKey() OrmKey
	GetDB() DB
	release() error
}

// 没有缓存相同的schema，都是创建一个
func newOrm(schema string, opts ...Option) (Orm, error) {
	// 处理选项字段
	if len(schema) == 0 {
		return nil, errors.New("dsn duplicate definition！")
	}
	options := defaultOption()
	// 塞入选项参数
	for _, opt := range opts {
		opt(options)
	}
	tp, dsn := GetDSN(schema)

	switch tp {
	case Mysql:
		// 设置mysql选项字段
		var mysqlConfig = options.mysqlConfig
		// 处理选项字段
		if len(mysqlConfig.DSN) != 0 {
			return nil, errors.New("dsn duplicate definition！")
		}
		mysqlConfig.DSN = dsn
		// 连接数据库引擎，并返回连接
		db, err := gorm.Open(mysql.New(mysqlConfig), &options.gormConfig)
		if err != nil {
			return nil, errors.New("failed to connect database, err: " + err.Error())
		}

		return &orm{
			schema: schema,
			ormKey: OrmKey(atomic.AddInt64(&num, 1)),
			db:     db,
		}, nil
	}
	return nil, UnSupport
}

type orm struct {
	schema string
	ormKey OrmKey
	db     *gorm.DB
}

func (o *orm) GetKey() OrmKey {
	return o.ormKey
}
func (o *orm) GetDB() DB {
	return o.db
}

func (o *orm) release() error {
	o.db = nil // 只能等待gorm内部处理了
	return nil
}
